package com.whh.starter.web;


import com.whh.starter.config.DefaultCodeMapper;
import com.whh.starter.config.RequestContext;
import com.whh.starter.config.response.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.hibernate.exception.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.stream.Collectors;

import static com.whh.starter.config.DefaultCodeMapper.PARAM_ERROR;
import static com.whh.starter.config.DefaultCodeMapper.UNEXPECTED_ERROR;


@ControllerAdvice
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(ResponseExceptionHandler.class);

    @Override
    public ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.info(" Error: handleExceptionInternal, {}", ex.getMessage());
        String message = null;
        if (ex instanceof HttpMessageNotReadableException) {
            if (StringUtils.isNotEmpty(ex.getMessage()) && ex.getMessage().contains("(")) {
                message = ex.getMessage().split("\\(")[0];
            }
        }
        if (message == null) {
            message = ex.getMessage();
        }
        ErrorResponse apiErrorResponse = new ErrorResponse().
                code(PARAM_ERROR.getCode()).message(message).
                messageId(RequestContext.getCurrentContext().getCorrelationId());
        RequestContext.getCurrentContext().error(PARAM_ERROR);
        return ResponseEntity.
                status(PARAM_ERROR.getStatus()).
                body(apiErrorResponse);
    }

    @Override
    public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.error("ex : {}", ex.getMessage(), ex);
        BindingResult bindingResult = ex.getBindingResult();
        String message = bindingResult
                .getFieldErrors()
                .stream().map(item -> item.getCode() + "error on '" + item.getField() + "' : " + item.getDefaultMessage())
                .collect(Collectors.joining(" | "));
        RequestContext.getCurrentContext().error(PARAM_ERROR);
        ErrorResponse response = new ErrorResponse()
                .code(PARAM_ERROR.getCode())
                .message(message)
                .messageId(RequestContext.getCurrentContext().getCorrelationId());
        log.info("Param Error: handleMethodArgumentNotValid, {}", message);
        return ResponseEntity
                .status(PARAM_ERROR.getStatus())
                .body(response);

    }

    /**
     * 数据库持久化 时验证失败
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException e) {
        ErrorResponse apiErrorResponse = new ErrorResponse().
                code(PARAM_ERROR.getCode()).
                message(e.getMessage()).
                messageId(RequestContext.getCurrentContext().getCorrelationId());
        RequestContext.getCurrentContext().error(PARAM_ERROR);
        log.info("Param Error: ConstraintViolationException, {}", e.getMessage());
        return ResponseEntity.
                status(PARAM_ERROR.getStatus()).
                body(apiErrorResponse);
    }


    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleException(Exception e) {
        log.info("Error: SystemException, {}", e.getMessage(),e);
        ErrorResponse apiErrorResponse = new ErrorResponse().
                code(UNEXPECTED_ERROR.getCode()).
                message(e.getMessage()).
                messageId(RequestContext.getCurrentContext().getCorrelationId());
        RequestContext.getCurrentContext().error(UNEXPECTED_ERROR);
        return ResponseEntity.
                status(UNEXPECTED_ERROR.getStatus()).
                body(apiErrorResponse);
    }
}
